/**
 * ***************************************************************************** Copyright (c) 2000,
 * 2008 IBM Corporation and others. All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * <p>Contributors: IBM Corporation - initial API and implementation Oakland Software (Francis
 * Upton) <francisu@ieee.org> - Fix for Bug 63149 [ltk] allow changes to be executed after the
 * 'main' change during an undo [refactoring]
 * *****************************************************************************
 */
package org.eclipse.ltk.core.refactoring.participants;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.internal.core.refactoring.ParticipantDescriptor;

/**
 * A refactoring participant can participate in the condition checking and change creation of a
 * {@link RefactoringProcessor}.
 *
 * <p>If the severity of the condition checking result is {@link RefactoringStatus#FATAL} then the
 * whole refactoring will not be carried out.
 *
 * <p>The changes created by a participant <em>MUST</em> not conflict with any changes provided by
 * other participants or the refactoring itself. To ensure this, a participant is only allowed to
 * manipulate resources belonging to its domain. As of 3.1 this got relaxed for textual resources. A
 * participant can now change a textual resource already manipulated by the processor as long as
 * both are manipulating different regions in the file (see {@link #createChange(IProgressMonitor)}
 * and {@link #getTextChange(Object)}). For example a rename type participant updating launch
 * configuration is only allowed to update launch configurations or shared textual resources. If a
 * change conflicts with another change during execution then the participant who created the change
 * will be disabled for the rest of the eclipse session.
 *
 * <p>A refactoring participant can not assume that all resources are saved before any methods are
 * called on it. Therefore a participant must be able to deal with unsaved resources.
 *
 * <p>A refactoring participant can implement {@link ISharableParticipant} in order to be shared for
 * multiple elements to be refactored by the same processor.
 *
 * <p>This class should be subclassed by clients wishing to provide special refactoring participants
 * extension points.
 *
 * <p>Since 3.4, a refactoring participant can also override {@link
 * #createPreChange(IProgressMonitor)} to add changes that will be executed <em>before</em> the main
 * refactoring changes are executed.
 *
 * @see RefactoringProcessor
 * @see ISharableParticipant
 * @since 3.0
 */
public abstract class RefactoringParticipant extends PlatformObject {

  private RefactoringProcessor fProcessor;

  private ParticipantDescriptor fDescriptor;

  /**
   * Returns the processor that is associated with this participant.
   *
   * @return the processor that is associated with this participant
   */
  public RefactoringProcessor getProcessor() {
    return fProcessor;
  }

  /**
   * Initializes the participant. This method is called by the framework when a participant gets
   * instantiated.
   *
   * <p>This method isn't intended to be extended or reimplemented by clients.
   *
   * @param processor the processor this participant is associated with
   * @param element the element to be refactored
   * @param arguments the refactoring arguments
   * @return <code>true</code> if the participant could be initialized; otherwise <code>false</code>
   *     is returned. If <code>false</code> is returned then the participant will not be added to
   *     the refactoring.
   * @see #initialize(Object)
   */
  public boolean initialize(
      RefactoringProcessor processor, Object element, RefactoringArguments arguments) {
    Assert.isNotNull(processor);
    Assert.isNotNull(arguments);
    fProcessor = processor;
    initialize(arguments);
    return initialize(element);
  }

  /**
   * Initializes the participant with the element to be refactored. If this method returns <code>
   * false</code> then the framework will consider the participant as not being initialized and the
   * participant will be dropped by the framework.
   *
   * @param element the element to be refactored
   * @return <code>true</code> if the participant could be initialized; otherwise <code>false</code>
   *     is returned.
   */
  protected abstract boolean initialize(Object element);

  /**
   * Initializes the participant with the refactoring arguments
   *
   * @param arguments the refactoring arguments
   */
  protected abstract void initialize(RefactoringArguments arguments);

  /**
   * Returns a human readable name of this participant.
   *
   * @return a human readable name
   */
  public abstract String getName();

  /**
   * Checks the conditions of the refactoring participant.
   *
   * <p>The refactoring is considered as not being executable if the returned status has the
   * severity of <code>RefactoringStatus#FATAL</code>. Note that this blocks the whole refactoring
   * operation!
   *
   * <p>Clients should use the passed {@link CheckConditionsContext} to validate the changes they
   * generate. If the generated changes include workspace resource modifications, clients should
   * call ...
   *
   * <pre> (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
   * IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();</pre>
   *
   * ... and use the delta factory to describe all resource modifications in advance.
   *
   * <p>This method can be called more than once.
   *
   * @param pm a progress monitor to report progress
   * @param context a condition checking context to collect shared condition checks
   * @return a refactoring status. If the status is <code>RefactoringStatus#FATAL</code> the
   *     refactoring is considered as not being executable.
   * @throws OperationCanceledException if the condition checking got canceled
   * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(IProgressMonitor)
   * @see RefactoringStatus
   */
  public abstract RefactoringStatus checkConditions(
      IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException;

  /**
   * Creates a {@link Change} object that contains the workspace modifications of this participant
   * to be executed <em>before</em> the changes from the refactoring are executed. Note that this
   * implies that the undo change of the returned Change object will be executed <em>after</em> the
   * undo changes from the refactoring have been executed.
   *
   * <p>The changes provided by a participant <em>must</em> not conflict with any change provided by
   * other participants or by the refactoring itself.
   *
   * <p>If the change conflicts with any change provided by other participants or by the refactoring
   * itself, then change execution will fail and the participant will be disabled for the rest of
   * the eclipse session.
   *
   * <p>If an exception occurs while creating the change, the refactoring can not be carried out,
   * and the participant will be disabled for the rest of the eclipse session.
   *
   * <p>A participant can manipulate text resource already manipulated by the processor as long as
   * the textual manipulations don't conflict (e.g. the participant manipulates a different region
   * of the text resource). The method must not return those changes in its change tree since the
   * change is already part of another change tree. If the participant only manipulates shared
   * changes then it can return <code>null</code> to indicate that it didn't create own changes. A
   * shared text change can be accessed via the method {@link #getTextChange(Object)}.
   *
   * <p>The default implementation returns <code>null</code>. Subclasses can extend or override.
   *
   * <p>Note that most refactorings will implement {@link #createChange(IProgressMonitor)} rather
   * than this method.
   *
   * @param pm a progress monitor to report progress
   * @return the change representing the workspace modifications to be executed before the
   *     refactoring change or <code>null</code> if no changes are made
   * @throws CoreException if an error occurred while creating the change
   * @throws OperationCanceledException if the change creation got canceled
   * @see #createChange(IProgressMonitor)
   * @since 3.4
   */
  public Change createPreChange(IProgressMonitor pm)
      throws CoreException, OperationCanceledException {
    return null;
  }

  /**
   * Creates a {@link Change} object that contains the workspace modifications of this participant
   * to be executed <em>after</em> the changes from the refactoring are executed. Note that this
   * implies that the undo change of the returned Change object will be executed <em>before</em> the
   * undo changes from the refactoring have been executed.
   *
   * <p>The changes provided by a participant <em>must</em> not conflict with any change provided by
   * other participants or by the refactoring itself.
   *
   * <p>If the change conflicts with any change provided by other participants or by the refactoring
   * itself, then change execution will fail and the participant will be disabled for the rest of
   * the eclipse session.
   *
   * <p>If an exception occurs while creating the change, the refactoring can not be carried out,
   * and the participant will be disabled for the rest of the eclipse session.
   *
   * <p>As of 3.1, a participant can manipulate text resources already manipulated by the processor
   * as long as the textual manipulations don't conflict (i.e. the participant manipulates a
   * different region of the text resource). The method must not return those changes in its change
   * tree since the change is already part of another change tree. If the participant only
   * manipulates shared changes, then it can return <code>null</code> to indicate that it didn't
   * create own changes. A shared text change can be accessed via the method {@link
   * #getTextChange(Object)}.
   *
   * @param pm a progress monitor to report progress
   * @return the change representing the workspace modifications to be executed after the
   *     refactoring change or <code>null</code> if no changes are made
   * @throws CoreException if an error occurred while creating the change
   * @throws OperationCanceledException if the change creation got canceled
   * @see #createPreChange(IProgressMonitor)
   */
  public abstract Change createChange(IProgressMonitor pm)
      throws CoreException, OperationCanceledException;

  /**
   * Returns the text change for the given element or <code>null</code> if a text change doesn't
   * exist. This method only returns a valid result during change creation. Outside of change
   * creation always <code>null</code> is returned.
   *
   * @param element the element to be modified for which a text change is requested
   * @return the text change or <code>null</code> if no text change exists for the element
   * @since 3.1
   */
  public TextChange getTextChange(Object element) {
    return getProcessor().getRefactoring().getTextChange(element);
  }

  // ---- helper method ----------------------------------------------------

  /* package */ void setDescriptor(ParticipantDescriptor descriptor) {
    Assert.isNotNull(descriptor);
    fDescriptor = descriptor;
  }

  /* package */ ParticipantDescriptor getDescriptor() {
    return fDescriptor;
  }
}
